﻿using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MugenInjection;
using MugenInjection.Core;
using MugenInjection.Core.Components;
using MugenInjection.Exceptions;
using MugenInjection.Interface;
using MugenInjection.Interface.Components;
using MugenInjectionTest.Infrastructure;
using Should;

#if !NOINTERCEPTORS
using MugenInjection.Interception;
using MugenInjectionTest.Interception;
#endif

namespace MugenInjectionTest.Core
{
    [TestClass]
    public class WorkWithModuleTest : TestBase
    {
        [TestMethod]
        public void TestLoadModule()
        {
            using (var injector = GetInjector())
            {
                var testModule = new TestModule();
                injector.Load(testModule);
                injector.GetBindings(typeof(InterfaceToBind)).Count().ShouldEqual(1);
                injector.ModuleIsLoaded(testModule.Name).ShouldBeTrue();
                injector.ModuleIsLoaded(typeof(TestModule)).ShouldBeTrue();

                TestUtils.Catch<Exception>(() => injector.Load(new TestModule()));

                //Unload
                injector.Unload(testModule.Name);
                injector.GetBindings(typeof(InterfaceToBind)).ShouldBeEmpty();
                injector.ModuleIsLoaded(testModule.Name).ShouldBeFalse();
                injector.ModuleIsLoaded(typeof(TestModule)).ShouldBeFalse();

                //Unload exception
                TestUtils.Catch<Exception>(() => injector.Unload(testModule.Name));
            }
        }

#if !NODYNAMICMODULE
        [TestMethod]
        public void TestAutoLoadModuleOnSettingChanges()
        {
            //Change attribute for auto load module settings
            var setting = new DefaultInjectorSetting { AttributeForAutoLoadModule = typeof(CompilerGeneratedAttribute) };
            using (var injector = GetInjector(setting))
            {
                injector.Bindings.Count.ShouldEqual(1);
                injector.ModuleIsLoaded(typeof(AutoLoadModuleTest)).ShouldBeTrue();
                injector.Get<InterfaceToBind>().ShouldEqual(AutoLoadModuleTest.InterfaceToBind);
            }

            //Custom settings
            setting.IsAutoLoadModuleEnabled = false;
            using (var injector = GetInjector(setting))
            {
                injector.ModuleIsLoaded(typeof(AutoLoadModuleTest)).ShouldBeFalse();
                injector.Bindings.ShouldBeEmpty();
                setting.IsAutoLoadModuleEnabled = true;

                injector.Bindings.Count.ShouldEqual(1);
                injector.ModuleIsLoaded(typeof(AutoLoadModuleTest)).ShouldBeTrue();
                injector.Get<InterfaceToBind>().ShouldEqual(AutoLoadModuleTest.InterfaceToBind);
            }
        }
#endif

#if !NOINTERCEPTORS
        [TestMethod]
        public void TestLoadDynamicModule()
        {
            using (var injector = GetInjector())
            {
                TestUtils.Catch<ArgumentNullException>(() => injector.Components.Get<IModuleManagerComponent>().LoadModules(null));
                TestUtils.Catch<ArgumentException>(() => injector.Components.Get<IModuleManagerComponent>().LoadModules(null));

                InterceptionExtensionUtils.Intercept(injector.Bind<TestModule>(), new SpyInterceptor());
                var testModule = injector.Get<TestModule>();
                TestUtils.Catch<BindingNotFoundException>(() => injector.Components.Get<IModuleManagerComponent>().LoadModules(testModule.GetType().GetTypeInfo().Assembly));
            }
        }
#endif
        [TestMethod]
        public void TestLoadComponent()
        {
            using (var injector = GetInjector())
            {
                var fakeComponent = new FakeComponent();
                TestModule.Component = fakeComponent;

                injector.Components.IsContain(fakeComponent).ShouldBeFalse();
                injector.Load(new TestModule());
                injector.Components.IsContain(fakeComponent).ShouldBeTrue();
                fakeComponent.CountLoad.ShouldEqual(1);
                fakeComponent.Injector.ShouldEqual(injector);

                injector.Unload(typeof(TestModule));
                injector.Components.IsContain(fakeComponent).ShouldBeFalse();
                fakeComponent.CountUnload.ShouldEqual(1);
                fakeComponent.Injector.ShouldBeNull();
            }
        }
    }


    public class InterfaceToBind
    {
    }

    public class ClassToBind : InterfaceToBind
    {
    }

    [CompilerGenerated]
    public class AutoLoadModuleTest : InjectorModule
    {
        #region Property

        public static readonly InterfaceToBind InterfaceToBind = new InterfaceToBind();

        #endregion

        #region Overrides of InjectorModule

        /// <summary>
        /// Load bindings and components in current module. 
        /// </summary>
        public override void Load()
        {
            Bind<InterfaceToBind>().ToConstant(InterfaceToBind);
        }

        #endregion
    }

    public class TestModule : InjectorModule
    {
        #region Properties

        public static IComponent Component { get; set; }

        #endregion

        #region Overrides of InjectorModule

        public override void Load()
        {
            Bind<InterfaceToBind>().To<ClassToBind>();
            if (Component != null)
                AddComponent(Component);
        }

        #endregion
    }
}